/*
 * @Author: hongbin
 * @Date: 2022-11-26 13:54:17
 * @LastEditors: hongbin
 * @LastEditTime: 2022-12-03 17:09:39
 * @Description:PartsControl 配件控制
 */

import { Color, MeshStandardMaterial } from "three";
import { get } from "../api";
import { IPart, IPartConfig, IUserPartsConfig } from "../components/CustomProfile/types";
import { UserInfo } from "../hook/useRegisterForm";
import { SocketManager } from "../socket";
import { loadGltf } from "./helper/loaderHelper";
import { otherUserList } from "./helper/OtherUser";
import { acterWrap } from "./object";
import { acterPlayer } from "./script";

/**
 * 配件控制器
 */
class PartsControl {
    parts: Record<string, IPart> = {};
    partsLength: number = 0;
    loadedPartsLength: number = 0;
    /**
     * 是否请求穿戴配件
     */
    isRequestWear: boolean = false;
    userPartsConfig: IUserPartsConfig | undefined;
    currentPartConfig: IUserPartsConfig["config1"] = { wearParts: [] } as unknown as IUserPartsConfig["config1"];
    actorGroup: THREE.Group | undefined;
    constructor() {}

    init(userId: string) {
        this.loadPartList();
        this.queryUserConfig(userId);
    }

    /**
     *  加载配件列表 装配用户
     */
    async loadPartList() {
        const { data: partsList } = await get<{ data: string[] }>("/parts/list");
        if (!partsList) return new Error("获取配件列表失败！");
        this.partsLength = partsList.length;
        partsList.forEach((partName) => {
            loadGltf(`api/parts/query?name=${partName}`).then((model) => {
                const animations = model.animations;
                const scene = model.scene;
                const { modelName: name, type } = model.scene.children[0].userData as Record<string, string>;

                const part = { animations, scene, name, type };
                this.parts[name] = part;
                this.loadedPartsLength++;
                if (this.loadedPartsLength == this.partsLength && this.isRequestWear) {
                    this.wear(this.actorGroup!);
                }
            });
        });
    }

    /**
     * 获取用户的配置
     */
    async queryUserConfig(userId: string) {
        const { data } = await get("/PartsConfig/query?id=" + userId);
        // console.log(data);
        this.userPartsConfig = data;
        if (this.loadedPartsLength == this.partsLength && this.isRequestWear) {
            this.wear(this.actorGroup!);
        }
    }

    /**
     * 穿着配件
     */
    wear(actorGroup: THREE.Group) {
        if (!actorGroup) return new Error("传入人物模型组出错");
        SocketManager.emit("IAmReady");
        this.sendSocket();
        if (!this.userPartsConfig) {
            return acterPlayer.init(actorGroup, actorGroup.animations);
        }
        this.actorGroup = actorGroup;
        // 校验是否加载用户配置和配件都加载完成可以穿戴
        //如果没加载完成 设置请求穿戴 在加载完成后 再次调用
        if (this.loadedPartsLength !== this.partsLength) {
            this.isRequestWear = true;
            return;
        }
        const currentConfig = this.userPartsConfig![this.userPartsConfig!.current];
        this.currentPartConfig = currentConfig;
        const wearParts = currentConfig.wearParts.map((name) => this.parts[name]);
        // console.log("配件信息:", wearParts, currentConfig);
        this.addPartsToActor(actorGroup, wearParts, currentConfig);
        SocketManager.listen("actorUpdate", (allInfo, spaceTine) => {
            otherUserList.setSpaceTine(spaceTine);
            otherUserList.updateData(allInfo);
            //@ts-ignore
            window.otherUserList = otherUserList;
        });
        SocketManager.listen("onLineUserNotEnough", () => {
            otherUserList.removeOtherUsers();
        });
    }

    sendSocket() {
        const user = JSON.parse(localStorage.getItem("userInfo") || "{}") as UserInfo;
        if (!user._id) return new Error("获取本地用户信息出错");
        const listen = () => {
            const { position, quaternion } = acterWrap;
            const config = partsControl.currentPartConfig;
            const info = {
                name: user.name,
                _id: user._id,
                quaternion,
                position,
                config,
            };

            SocketManager.emit("uploadActor", info);
        };
        SocketManager.listen("uploadActor", listen);
        otherUserList.setSelf(user._id);
    }

    /**
     * 添加配件到人物模型上
     */
    addPartsToActor(actorGroup: THREE.Group, wearParts: IPart[], partsConfig: Record<string, IPartConfig>) {
        const animations: THREE.AnimationClip[] = [];
        wearParts.forEach((part) => {
            const config = partsConfig[part.name];
            const mesh = part.scene.children[0] as THREE.Mesh;
            if (!mesh) return console.error("没有mesh:", part);
            const material = mesh.material as THREE.MeshStandardMaterial;
            material.color = new Color(config.color);
            material.transparent = true;
            material.opacity = config.opacity;
            material.metalness = config.metalness;
            actorGroup.add(part.scene);

            animations.push(...part.animations);
        });

        acterPlayer.init(actorGroup, [...animations, ...actorGroup.animations]);
        window.render();
    }
}

export const partsControl = new PartsControl();

/**
 * 环境内其他玩家的配件着装 不需要加载配件 partsControl 已经加载完毕
 */
export function otherUserWear(
    group: THREE.Group,
    partsConfig: IUserPartsConfig["config1"],
    animationControl: typeof acterPlayer,
    actorAnimations: THREE.AnimationClip[]
) {
    const wearParts = partsConfig.wearParts.map((name) => partsControl.parts[name]);
    const animations: THREE.AnimationClip[] = [];
    wearParts.forEach((part) => {
        const mesh = part.scene.children[0] as THREE.Mesh;
        if (!mesh) return console.error("没有mesh:", part);
        //不能直接使用 同一个模型只能有一个 使用clone体
        const config = partsConfig[part.name];
        const clonesMesh = mesh.clone(true) as THREE.Mesh;

        const material = new MeshStandardMaterial({ flatShading: true });
        material.color = new Color(config.color);
        material.transparent = true;
        material.opacity = config.opacity;
        material.metalness = config.metalness;

        clonesMesh.material = material;

        group.add(clonesMesh);
        //动画可以使用一个 并且克隆体没有动画
        animations.push(...part.animations);
    });
    animationControl.init(group, [...animations, ...actorAnimations]);
}
